/*

* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Smart-Shop开源商城系统/ Java商城-首选电商平台系统 请务必保留此注释头信息
* |     开源地址: https://gitee.com/ningbo-qishan/gruul-mall
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     可正常分享和学习源码,未经授权 不可商用！
* |     商业版中台购买/开源版商业授权 联系技术客服
* |     官网:  https://www.73app.cn/
* ------------------------------------------------------------------------
* ------------------------------------------------------------------------
* |     Copyright (c) 2016-2999 宁波启山智软科技有限公司
* |     版权所有 ,侵权必究！
* ------------------------------------------------------------------------

*/
package com.medusa.gruul.common.core.config;

import cn.hutool.core.util.StrUtil;
import com.medusa.gruul.common.core.annotation.EscapeLogin;
import com.medusa.gruul.common.core.auth.client.context.AuthPlatformContextHolder;
import com.medusa.gruul.common.core.auth.client.helper.ISecurity;
import com.medusa.gruul.common.core.auth.model.CompleteUser;
import com.medusa.gruul.common.core.auth.model.User;
import com.medusa.gruul.common.core.auth.model.constant.ClaimsKeys;
import com.medusa.gruul.common.core.auth.model.constant.Headers;
import com.medusa.gruul.common.core.auth.model.constant.Keys;
import com.medusa.gruul.common.core.constant.enums.AuthCodeEnum;
import com.medusa.gruul.common.core.exception.ServiceException;
import com.medusa.gruul.common.core.helper.AccessToken;
import com.medusa.gruul.common.core.helper.ISecurityUserService;
import com.medusa.gruul.common.core.helper.JwtHelper;
import com.medusa.gruul.common.core.helper.RedisKeyHelper;
import com.medusa.gruul.common.core.util.Result;
import com.medusa.gruul.common.core.util.SystemCode;
import com.medusa.gruul.common.log.annotation.SysLog;
import com.medusa.gruul.common.redis.RedisManager;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

/**
 * @author 张治保
 * date 2021/12/13
 */
@RequiredArgsConstructor
@RestController
public class AuthServerRestController {

    private final ISecurityUserService securityUserService;
    @EscapeLogin
    @SysLog("解析token数据")
    @GetMapping("/auth/token/user")
    public Result<CompleteUser> getUser(){
        String token = ISecurity.token().must();
        /*
         * 设置当前token
         */
        Claims claims;
        try {
            claims = JwtHelper.tokenHelper.getClaim(token);
        } catch (ExpiredJwtException expired) {
            throw new ServiceException(AuthCodeEnum.AUTH_EXPIRES.getDescription(),AuthCodeEnum.AUTH_EXPIRES.getType());
        } catch (Exception e) {
            throw new ServiceException(AuthCodeEnum.AUTH_FAIL.getDescription(),AuthCodeEnum.AUTH_FAIL.getType());
        }
        Object userO = claims.get(ClaimsKeys.USER);
        String userStr;
        if (!(userO instanceof String) || StrUtil.isBlank(userStr = (String) userO)) {
            throw new ServiceException(AuthCodeEnum.AUTH_FAIL.getDescription(),AuthCodeEnum.AUTH_FAIL.getType());
        }
        User user = Jackson.readValue(userStr, User.class);
        /*
         * jti比较
         */
        String jti = (String) claims.get(ClaimsKeys.JTI);
        if (StrUtil.isBlank(jti)){
            throw new ServiceException(AuthCodeEnum.AUTH_FAIL.getDescription(),AuthCodeEnum.AUTH_FAIL.getType());
        }
        String redisJti = RedisManager.getInstance().get(RedisKeyHelper.key(Keys.USER_PLATFORM_JTI, ISecurity.platform().must().name(), user.getUserId()));
        if (StrUtil.isBlank(redisJti)){
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(),AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }
        if (!jti.equals(redisJti)){
            throw new ServiceException(AuthCodeEnum.LOGGED_IN_ELSEWHERE.getDescription(), AuthCodeEnum.LOGGED_IN_ELSEWHERE.getType());
        }
        /*
         * 正确返回
         */
        return Result.ok(
            new CompleteUser().setJti(jti).setUserStr(userStr)
        );
    }

    @EscapeLogin
    @SysLog("刷新token")
    @GetMapping("/auth/token/refresh")
    public Result<AccessToken> refresh(@RequestHeader(Headers.REFRESH_TOKEN) String refreshToken){
        if (StrUtil.isEmpty(refreshToken)) {
            throw new ServiceException(SystemCode.PARAM_MISS);
        }
        Claims claims;
        try {
            claims = JwtHelper.refeshTokenHelper.getClaim(refreshToken);
        } catch (ExpiredJwtException expired) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_EXPIRES.getDescription(), AuthCodeEnum.AUTH_REFRESH_EXPIRES.getType());
        } catch (Exception e) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(), AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }
        Object user = claims.get(ClaimsKeys.USER);
        String userStr;
        if (!(user instanceof String) || StrUtil.isBlank(userStr = (String) user)) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(), AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }
        User userOld;
        try {
            userOld = Jackson.readValue(userStr, User.class);
        } catch (Exception e) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(), AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }

        if (!claims.getSubject().equals(userOld.getUserId()) || AuthPlatformContextHolder.get() != userOld.getAuthPlatform()) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(), AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }
        /*
         * 返回新的token与refresh token
         */
        AccessToken token = securityUserService.getUser(userOld.getId());
        if (token == null) {
            throw new ServiceException(AuthCodeEnum.AUTH_REFRESH_FAIL.getDescription(), AuthCodeEnum.AUTH_REFRESH_FAIL.getType());
        }
        return Result.ok(token);
    }
}
